/*
 *
 * Copyright (c) 2020 The Raptor Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "src/common/endpoint_impl.h"

#include <string.h>

#include "raptor-lite/impl/container.h"
#include "raptor-lite/impl/endpoint.h"

#include "src/common/connection_id.h"
#include "src/common/socket_util.h"

#ifdef _WIN32
#include "src/windows/socket_settings.h"
#else
#include "src/linux/socket_settings.h"
#endif

namespace raptor {

EndpointImpl::EndpointImpl(uint64_t fd, raptor_resolved_address *local,
                           raptor_resolved_address *remote)
    : fd_(fd)
    , connection_id_(internal::InvalidConnectionId)
    , container_(nullptr) {
    listen_port_ = 0;
    private_ctx_ = 0;
    memcpy(&local_, local, sizeof(raptor_resolved_address));
    memcpy(&remote_, remote, sizeof(raptor_resolved_address));
}

EndpointImpl::~EndpointImpl() {}

void EndpointImpl::SetConnectionId(uint32_t connection_id) { connection_id_ = connection_id; }
void EndpointImpl::SetContainer(Container *container) { container_ = container; }
void EndpointImpl::SetListenPort(uint16_t port) { listen_port_ = port; }
uint32_t EndpointImpl::ConnectionId() { return connection_id_; }
uint64_t EndpointImpl::SocketFd() { return fd_; }
uint16_t EndpointImpl::GetListenPort() { return listen_port_; }

std::string EndpointImpl::PeerString() {
    char *ptr = nullptr;
    int ret = raptor_sockaddr_to_string(&ptr, &remote_, 1);
    if (ptr) {
        std::string peer(ptr, ret);
        free(ptr);
        return peer;
    }
    return std::string();
}

std::string EndpointImpl::LocalString() {
    char *ptr = nullptr;
    int ret = raptor_sockaddr_to_string(&ptr, &local_, 1);
    if (ptr) {
        std::string peer(ptr, ret);
        free(ptr);
        return peer;
    }
    return std::string();
}

void EndpointImpl::BindContainer(Container *container) {
    container->AttachEndpoint(GetEndpointImpl());
}

bool EndpointImpl::SendMsg(const Slice &slice) {
    if (container_) {
        return container_->SendMsg(connection_id_, slice);
    }
    return false;
}

bool EndpointImpl::SendMsg(const void *data, size_t len) {
    if (container_) {
        return container_->SendMsg(connection_id_, Slice(data, len));
    }
    return false;
}

int EndpointImpl::SyncSend(const void *data, size_t len) {
#ifdef _WIN32
    return ::send((SOCKET)fd_, (const char *)data, (int)len, 0);
#else
    return ::send((int)fd_, data, len, 0);
#endif
}

int EndpointImpl::SyncRecv(void *data, size_t len) {
#ifdef _WIN32
    return ::recv((SOCKET)fd_, (char *)data, (int)len, 0);
#else
    return ::recv((int)fd_, data, len, 0);
#endif
}

bool EndpointImpl::Close(bool notify) {
    if (container_) {
        container_->CloseEndpoint(GetEndpointImpl(), notify);
    } else {
#ifdef _WIN32
        raptor_set_socket_shutdown((SOCKET)fd_);
#else
        raptor_set_socket_shutdown((int)fd_);
#endif
        fd_ = uint64_t(~0);
    }
    return true;
}

std::string EndpointImpl::LocalIp() {
    char ip[128] = {0};
    int ret = raptor_sockaddr_get_ip(&local_, ip, sizeof(ip));
    return std::string(ip, ret);
}

uint16_t EndpointImpl::LocalPort() {
    return static_cast<uint16_t>(raptor_sockaddr_get_port(&local_));
}

std::string EndpointImpl::RemoteIp() {
    char ip[128] = {0};
    int ret = raptor_sockaddr_get_ip(&remote_, ip, sizeof(ip));
    return std::string(ip, ret);
}

uint16_t EndpointImpl::RemotePort() {
    return static_cast<uint16_t>(raptor_sockaddr_get_port(&remote_));
}

bool EndpointImpl::IsValid() { return fd_ != uint64_t(~0) && fd_ > 0; }
void EndpointImpl::SetPrivateContext(intptr_t ctx) { private_ctx_ = ctx; }
intptr_t EndpointImpl::PrivateContext() { return private_ctx_; }

} // namespace raptor
